package com.skymobi.stc.redis;

import java.lang.reflect.Field;
import java.util.List;

import javax.management.Attribute;
import javax.management.AttributeList;
import javax.management.AttributeNotFoundException;
import javax.management.DynamicMBean;
import javax.management.InvalidAttributeValueException;
import javax.management.MBeanAttributeInfo;
import javax.management.MBeanConstructorInfo;
import javax.management.MBeanException;
import javax.management.MBeanInfo;
import javax.management.ReflectionException;

import org.apache.commons.pool2.BasePooledObjectFactory;
import org.apache.commons.pool2.PooledObject;
import org.apache.commons.pool2.impl.DefaultPooledObject;
import org.apache.commons.pool2.impl.GenericObjectPool;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import redis.clients.jedis.Jedis;
import redis.clients.util.Pool;

import com.google.common.base.Preconditions;
import com.google.common.collect.Lists;

public class Jedis2Pool extends Pool<Jedis2> {

	private static final Logger logger = LoggerFactory
			.getLogger(Jedis2Pool.class);

	class RedisPoolStaticticsMBean implements DynamicMBean {

		GenericObjectPool pool = null;

		RedisPoolStaticticsMBean() {
			try {

				for (Field field : Pool.class.getDeclaredFields()) {
					if (field.getName().equals("internalPool")) {
						field.setAccessible(true);
						pool = (GenericObjectPool) field.get(Jedis2Pool.this);
					}
				}
			} catch (Exception e) {
				logger.error("Cannot get vaue of field['internalPool'].", e);
				return;
			}

		}

		/*
		 * 
		 * @see javax.management.DynamicMBean#getAttribute(java.lang.String)
		 */
		public Object getAttribute(String attribute)
				throws AttributeNotFoundException, MBeanException,
				ReflectionException {
			if (pool == null) {
				return null;
			}

			if (attribute.equals("maxTotal")) {
				return pool.getMaxTotal();
			}

			if (attribute.equals("maxIdle")) {
				return pool.getMaxIdle();
			}

			if (attribute.equals("maxWaitMillis")) {
				return pool.getMaxWaitMillis();
			}

			if (attribute.equals("minIdle")) {
				return pool.getMinIdle();
			}

			if (attribute.equals("numActive")) {
				return pool.getNumActive();
			}

			if (attribute.equals("numIdle")) {
				return pool.getNumIdle();
			}

			return null;
		}

		/**
		 * 
		 * @see javax.management.DynamicMBean#setAttribute(javax.management.Attribute
		 *      )
		 */
		public void setAttribute(Attribute attribute)
				throws AttributeNotFoundException,
				InvalidAttributeValueException, MBeanException,
				ReflectionException {

		}

		/**
		 * 
		 * @see javax.management.DynamicMBean#getAttributes(java.lang.String[])
		 */
		public AttributeList getAttributes(String[] attributes) {
			Preconditions.checkArgument(attributes != null
					&& attributes.length > 0);
			AttributeList al = new AttributeList();
			for (String attribute : attributes) {
				Object value = null;
				try {
					value = this.getAttribute(attribute);
				} catch (Exception e) {
					logger.error("Cannot get attribute[" + attribute
							+ "] value.", e);
					continue;
				}
				al.add(new Attribute(attribute, value));
			}
			return al;
		}

		/**
		 * 
		 * @see javax.management.DynamicMBean#setAttributes(javax.management.
		 *      AttributeList)
		 */
		public AttributeList setAttributes(AttributeList attributes) {
			return null;
		}

		/**
		 * 
		 * @see javax.management.DynamicMBean#invoke(java.lang.String,
		 *      java.lang.Object[], java.lang.String[])
		 */
		public Object invoke(String actionName, Object[] params,
				String[] signature) throws MBeanException, ReflectionException {
			return null;
		}

		/**
		 * 
		 * @see javax.management.DynamicMBean#getMBeanInfo()
		 */
		public MBeanInfo getMBeanInfo() {
			List<MBeanAttributeInfo> infoLists = Lists.newArrayList();
			infoLists.add(new MBeanAttributeInfo("maxTotal",
					"java.lang.Integer", null, true, false, false));
			infoLists.add(new MBeanAttributeInfo("maxIdle",
					"java.lang.Integer", null, true, false, false));
			infoLists.add(new MBeanAttributeInfo("maxWaitMillis",
					"java.lang.Long", null, true, false, false));
			infoLists.add(new MBeanAttributeInfo("minIdle",
					"java.lang.Integer", null, true, false, false));
			infoLists.add(new MBeanAttributeInfo("numActive",
					"java.lang.Integer", null, true, false, false));
			infoLists.add(new MBeanAttributeInfo("numIdle",
					"java.lang.Integer", null, true, false, false));

			List<MBeanConstructorInfo> constructorInfos = Lists.newArrayList();
			constructorInfos.add(new MBeanConstructorInfo(
					"RedisPoolStaticticsMBean()", null, null));

			return new MBeanInfo("RedisPoolStatictics", null,
					(MBeanAttributeInfo[]) infoLists
							.toArray(new MBeanAttributeInfo[infoLists.size()]),
					(MBeanConstructorInfo[]) constructorInfos
							.toArray(new MBeanConstructorInfo[constructorInfos
									.size()]), null, null);

		}

	}

	private static class Jedis2Factory extends BasePooledObjectFactory<Jedis2> {

		private final String host;
		private final int port;
		private final int timeout;
		private final String password;

		public Jedis2 create() throws Exception {
			Jedis2 jedis = null;
			if (timeout > 0) {
				jedis = new Jedis2(host, port, timeout);
			} else {
				jedis = new Jedis2(host, port);
			}
			jedis.connect();
			if (null != password && password.trim().length() > 0) {
				jedis.auth(password);
			}
			return jedis;
		}

		public PooledObject<Jedis2> wrap(Jedis2 obj) {
			return new DefaultPooledObject<Jedis2>(obj);
		}

		public void destroyObject(PooledObject<Jedis2> obj) throws Exception {
			Jedis2 jedis = obj.getObject();
			if (jedis.isConnected()) {
				try {
					try {
						jedis.quit();
					} catch (Exception e) {
					}
					jedis.disconnect();
				} catch (Exception e) {
				}
			}
		}

		public boolean validateObject(PooledObject<Jedis2> obj) {
			final Jedis jedis = obj.getObject();
			try {
				return jedis.isConnected() && jedis.ping().equals("PONG");
			} catch (final Exception e) {
				return false;
			}

		}

		public Jedis2Factory(String host, int port, int timeout, String password) {
			this.host = host;
			this.port = port;
			this.timeout = timeout <= 0 ? -1 : timeout;
			this.password = password;
		}
	}

	public Jedis2Pool(GenericObjectPoolConfig poolConfig, String host) {
		this(poolConfig, host, 6379, 2000, null);
	}

	public Jedis2Pool(String host, int port) {
		super(new GenericObjectPoolConfig(), new Jedis2Factory(host, port,
				2000, null));
	}

	public Jedis2Pool(GenericObjectPoolConfig poolConfig, String host,
			int port, int timeout, String password) {
		super(poolConfig, new Jedis2Factory(host, port, timeout, password));
	}

	public Jedis2Pool(GenericObjectPoolConfig poolConfig, String host, int port) {
		this(poolConfig, host, port, 2000, null);
	}

	public Jedis2Pool(GenericObjectPoolConfig poolConfig, String host,
			int port, int timeout) {
		this(poolConfig, host, port, timeout, null);
	}

	public RedisPoolStaticticsMBean getRedisPoolStatictics() {
		return new RedisPoolStaticticsMBean();
	}
}
